#include "config.h"

#include "util.h"
#include "CodeGen.h"

#include "contrib/tinyxml2.h"
#include "rutil/Log.hxx"
#include "rutil/Logger.hxx"

#include "popt.h"

#include <signal.h>
#include <iostream>
#include <fstream>
#include <limits>

using namespace codegen;
using namespace tinyxml2;
using namespace resip;
using namespace std;

#define RESIPROCATE_SUBSYSTEM LogSystem::LOGMODULE

void TestTinyXml2(int argc, char* argv[]);

static bool finished;
static void signalHandler(int signo)
{
    std::cerr << "Caught control signal (" << signo << "). Shutting down..." << endl;
    finished = true;
}

bool registerSignal()
{
    // Install signal handlers
#ifndef _WIN32
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
    {
        cerr << "Couldn't install signal handler for SIGPIPE" << endl;
        return false;
    }
    if (signal(SIGHUP, signalHandler) == SIG_ERR)
    {
        cerr << "Couldn't install signal handler for SIGHUP" << endl;
        return false;
    }
#endif

    if (signal(SIGINT, signalHandler) == SIG_ERR)
    {
        cerr << "Couldn't install signal handler for SIGINT" << endl;
        return false;
    }

    if (signal(SIGTERM, signalHandler) == SIG_ERR)
    {
        cerr << "Couldn't install signal handler for SIGTERM" << endl;
        return false;
    }

    return true;
}

static void displayArgs(poptContext con,
    /*@unused@*/ enum poptCallbackReason foo,
    struct poptOption * key,
    /*@unused@*/ const char * arg, /*@unused@*/ void * data)
    /*@globals fileSystem@*/
    /*@modifies fileSystem@*/
{
    if (key->shortName == 'h')
        poptPrintHelp(con, stdout, 0);
    else
        poptPrintUsage(con, stdout, 0);
    exit(0);
}

void enable_log(const resip::Data& file)
{
    if (!file.empty())
    {
        Log::initialize(Log::File, Log::Info, "", file.c_str());
    }
}

int main(int argc, char* argv[])
{
    registerSignal();

    Log::initialize(Log::File, Log::None, "");

    struct poptOption help_table[] = {
            { NULL,     '\0',   POPT_ARG_CALLBACK, (void *)&displayArgs,    '\0',   NULL,                           NULL },
            { "help",   'h',    0,                  NULL,                   '?',    "Show this help message",       NULL },
            { "usage",  'u',    0,                  NULL,                   'u',    "Display brief usage message",  NULL },
            POPT_TABLEEND
    };

    struct poptOption indent_table[] = {
        {"disable-indent",   0,         POPT_ARG_NONE,      0,  'i', "Whether to disable indentation", 0},
        {"reserve-tab",      0,         POPT_ARG_NONE,      0,  'r', "Whether to reserver tab as indentation leading, effective when --disable-indent is specified", 0},
        {"space",           's',        POPT_ARG_INT,       0,  's', "Replace tabs with spaces counted by SIZE as indentation, effective when --disable-indent is specified", "SIZE"},
        POPT_TABLEEND
    };

    struct poptOption table[] = {
        {"base",            'b',        POPT_ARG_STRING,        0,              'b', "The class name of the parent class that needs to be inherited", 0},
        {"type",            't',        POPT_ARG_STRING,        0,              't', "Type of in file content", "json|xml|auto"},
        {"ext",             'e',        POPT_ARG_STRING,        0,              'e', "Extension of out file, such as `.h` or `.hxx` or `.hpp`, default is `.h`", 0},
        {"default",         'd',        POPT_ARG_NONE,          0,              'd',
        "Whether to use value in file to assign member value as default value when construct object. Otherwise, use 0 value corresponding to the member type. default is `false`", 0},
        { NULL,            '\0',        POPT_ARG_INCLUDE_TABLE, indent_table,   0,   "Options for indentation", 0},
        {"log",             'l',        POPT_ARG_STRING,        0,              'l', "Output generation log to FILE", "FILE"},
        {"version",         'v',        POPT_ARG_NONE,          0,              'v', "Show version", 0},
        { NULL,            '\0',        POPT_ARG_INCLUDE_TABLE, help_table,     0,   "Options for help", 0},
        POPT_TABLEEND
    };
    poptContext context = poptGetContext(NULL, argc, const_cast<const char**>(argv), table, 0);
    poptSetOtherOptionHelp(context, " [OPTION...] file/directory");
    
    resip::Data base_class_name;
    GenConfig::GenType type = GenConfig::AUTO;
    // generate out file extension
    resip::Data out_file_ext = ".h";
    // use value in file to assign member value as default value when construct object.
    //   otherwise, use 0 value corresponding to the member type
    bool use_file_value = false;
    // indent parameters
    bool enable_indent = true;
    bool reserve_tab = false;
    unsigned int space_count = 4;

    int ret = 0;
    while ((ret = poptGetNextOpt(context)) >= 0)
    {
        const char* arg = poptGetOptArg(context);
        switch (ret)
        {
        case 'b':
            base_class_name = arg;
            break;
        case 't':
            type = PathInfo::get_type(arg);
            break;
        case 'd':
            use_file_value = true;
        case 'e':
            out_file_ext = arg;
            break;
        case 'i':
            enable_indent = false;
            break;
        case 'r':
            reserve_tab = true;
            break;
        case 's':
            space_count = Data(arg).convertInt();
            break;
        case 'l':
            enable_log(arg);
            break;
        case 'v':
            cout << CODEGEN_INT_VERSION << endl;
            return 0;
            break;
        default:
            break;
        }
    }

    if (ret < -1)
    {
        cout << poptStrerror(ret) << ": " << poptBadOption(context, POPT_BADOPTION_NOALIAS);
        return 0;
    }

    const char* arg = poptGetArg(context);
    if (!arg)
    {
        cout << "missing file arguments" << endl
            << "Try `" << argv[0] << " --help` for more information." << endl;
        return 0;
    }

    GenConfig::instance().set(base_class_name, type, out_file_ext, use_file_value, enable_indent, reserve_tab, space_count);

    CodeGen gen(arg);
    gen.generate();

    return 0;
}

void TestTinyXml2(int argc, char* argv[])
{
    if (argc > 1)
    {
        XMLDocument doc;
        XMLError err = doc.LoadFile(argv[1]);
        if (XML_SUCCESS != err)
        {
            cout << "parse error:" << XMLDocument::ErrorIDToName(err) << endl;
            return;
        }
#if 0
        XMLPrinter visitor;
        doc.Print(&visitor);
        cout << visitor.CStr() << endl;
#endif
        XMLElement* cur = doc.FirstChildElement();

        XMLElement* e = cur->FirstChildElement("RTCPFeedback");
        if (e)
        {
#if 0
            const XMLAttribute* attr = e->FindAttribute("NackPli");
            if (attr)
            {
                cout << attr->Name() << ":" << attr->Value() << endl;
            }
#endif
            e = e->FirstChildElement("NackPli");
            if (e)
            {
                cout << e->Name() << ":" << e->GetText() << endl;
            }
        }

        e = cur->FirstChildElement("MimeType");
        cout << e->Name() << ":" << e->GetText() << endl;
    }
    else
    {
        cout << "usage:" << argv[0] << " xml file" << endl;
    }
}

